package com.sd365.permission.centre.service.impl;

import com.alibaba.fastjson.JSON;
import com.github.pagehelper.Page;
import com.sd365.common.core.annotation.mybatis.Pagination;
import com.sd365.common.core.annotation.stuffer.CommonFieldStuffer;
import com.sd365.common.core.annotation.stuffer.IdGenerator;
import com.sd365.common.core.annotation.stuffer.MethodTypeEnum;
import com.sd365.common.core.common.advice.MyPageInfo;
import com.sd365.common.core.common.exception.BusinessException;
import com.sd365.common.core.common.exception.code.BizErrorCode;
import com.sd365.common.core.common.exception.code.CommonErrorCode;
import com.sd365.common.core.common.service.AbstractBusinessService;
import com.sd365.common.core.context.BaseContextHolder;
import com.sd365.common.util.BeanUtil;
import com.sd365.permission.centre.dao.mapper.BlackOrWhiteMapper;
import com.sd365.permission.centre.entity.BlackOrWhite;
import com.sd365.permission.centre.pojo.dto.BlackOrWhiteDTO;
import com.sd365.permission.centre.pojo.query.BlackOrWhiteQuery;
import com.sd365.permission.centre.pojo.vo.BlackOrWhiteVO;
import com.sd365.permission.centre.pojo.vo.UserVO;
import com.sd365.permission.centre.service.BlackOrWhiteService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import tk.mybatis.mapper.entity.Example;

import java.util.ArrayList;
import java.util.List;

import static org.springframework.util.Assert.isTrue;
import static org.springframework.util.Assert.notEmpty;
/**
 * @Class BlackOrWhiteServiceImpl
 * @Description  实现了黑名名单的数据的管理，UI 和 网关的过滤器都将调用该模块的数据
 * TODO 当前类的剩余cache部分还有编写，请检查 黑白名单的存入redis的代码
 * @Author Administrator
 * @Date 2023-03-14  20:53
 * @version 1.0.0
 */
@Service
public class BlackOrWhiteServiceImpl extends AbstractBusinessService implements BlackOrWhiteService {
    /**
     *  黑名名单dao
     */
    @Autowired
    private BlackOrWhiteMapper blackOrWhiteMapper;

    /**
     * id生成器
     */
    @Autowired
    private IdGenerator idGenerator;
    /**
     * redis 操作，用于提高访问速度 网关的黑名名单过滤器将使用redis
     */
    @Autowired
    private RedisTemplate redisTemplate;


    @Pagination
    @Override
    public List<BlackOrWhiteVO> commonQuery(BlackOrWhiteQuery blackOrWhiteQuery) {

        List<BlackOrWhite> blackOrWhites = blackOrWhiteMapper.commonQuery(blackOrWhiteQuery);
        setPageInfo2BaseContextHolder((Page)blackOrWhites);
        if (!CollectionUtils.isEmpty(blackOrWhites)) {
            return BeanUtil.copyList(blackOrWhites, BlackOrWhiteVO.class, new BeanUtil.CopyCallback() {
                @Override
                public void copy(Object o, Object o1) {
                    BlackOrWhite entity = (BlackOrWhite) o;
                    BlackOrWhiteVO vo = (BlackOrWhiteVO) o1;
                    if (entity.getUser() != null) {
                        BeanUtil.copy(entity.getUser(), vo.getUserVO(), UserVO.class);
                    }
                }
            });
        }
        return new ArrayList<>();
    }


    @Override
    public BlackOrWhiteVO add(BlackOrWhiteDTO blackOrWhiteDTO) {
        if(
                blackOrWhiteMapper.selectByExample(new Example(BlackOrWhite.class).createCriteria()
                .andEqualTo("",blackOrWhiteDTO.getBlackOrWhiteAccount())).isEmpty()){
           throw new BusinessException(BizErrorCode.DATA_SEARCH_FOUND_MANNY,new RuntimeException("同一账号的黑名单名单记录已经存在"));
        }
        BlackOrWhite blackOrWhite = BeanUtil.copy(blackOrWhiteDTO, BlackOrWhite.class);
//        Object o = redisTemplate.opsForValue().get(blackOrWhite.getBlackOrWhiteAccount());
        blackOrWhite.setId(idGenerator.snowflakeId());
        blackOrWhite.setVersion(1L);
        super.baseDataStuff4Add(blackOrWhite);
        super.baseDataStuffCommonPros(blackOrWhite);
        if (blackOrWhiteMapper.insert(blackOrWhite) > 0) {
            return BeanUtil.copy(blackOrWhite, BlackOrWhiteVO.class);
        } else {return new BlackOrWhiteVO();}
    }

    @Override
    public Boolean remove(Long id, Long version) {
        BlackOrWhite blackOrWhite = blackOrWhiteMapper.selectByPrimaryKey(id);
        removeCache(blackOrWhite.getBlackOrWhiteAccount());
        return blackOrWhiteMapper.deleteByPrimaryKey(id) > 0;
    }



    @Override
    public Boolean removeCache(String account) {
        return true;
    }

    @CommonFieldStuffer(methodType = MethodTypeEnum.DELETE)
    @Override
    public Boolean batchRemove(BlackOrWhiteDTO[] blackOrWhiteDTOS) {
        notEmpty(blackOrWhiteDTOS, "批量删除参数不可以为空");
        for (BlackOrWhiteDTO blackOrWhiteDTO : blackOrWhiteDTOS) {
            BlackOrWhite copy = BeanUtil.copy(blackOrWhiteDTO, BlackOrWhite.class);
            // 删除数据库数据并且移除redis数据
            int result = blackOrWhiteMapper.deleteByPrimaryKey(copy);
            removeCache(blackOrWhiteDTO.getBlackOrWhiteAccount());
            isTrue(result > 0, String.format("删除的记录id %d 没有匹配到版本", blackOrWhiteDTO.getId()));
        }
        return true;
    }

    @Override
    public Boolean modify(BlackOrWhiteDTO blackOrWhiteDTO) {
        BlackOrWhite copy = BeanUtil.copy(blackOrWhiteDTO, BlackOrWhite.class);
        super.baseDataStuff4Updated(copy);
        super.baseDataStuffCommonPros(copy);
        int affectRow= blackOrWhiteMapper.updateByPrimaryKeySelective(copy);
        if(affectRow>0){
            redisTemplate.opsForValue().set(blackOrWhiteDTO.getBlackOrWhiteAccount(),blackOrWhiteDTO);
        }else {
            throw  new BusinessException(BizErrorCode.DATA_UPDATE_RETURB_EFFECT_LATTER_ZERO,
                    new RuntimeException("blackOrWhiteMapper.updateByPrimaryKeySelective 更新数据库返回0"));
        }

        return Boolean.TRUE;
    }

    @Transactional
    @Override
    public Boolean batchModify(BlackOrWhiteDTO[] blackOrWhiteDTOS) {
        Assert.noNullElements(blackOrWhiteDTOS, "更新数组不能为空");
        for (BlackOrWhiteDTO blackOrWhiteDTO : blackOrWhiteDTOS) {
            BlackOrWhite blackOrWhite = BeanUtil.copy(blackOrWhiteDTO, BlackOrWhite.class);
            Example example = new Example(blackOrWhite.getClass());
            example.createCriteria().andEqualTo("id", blackOrWhite.getId()).andEqualTo("version", blackOrWhite.getVersion());
            super.baseDataStuff4Updated(blackOrWhite);
            super.baseDataStuffCommonPros(blackOrWhite);
            if (blackOrWhiteMapper.updateByExampleSelective(blackOrWhite,example)<=0){
                throw new BusinessException(BizErrorCode.DATA_UPDATE_RETURB_EFFECT_LATTER_ZERO,new RuntimeException("批量更新黑白名单错误"));
            }
        }
        // 同步更新缓存 如果异常则整个事务被回滚
        List<String> accountList=new ArrayList<>();
        try {
            for (BlackOrWhiteDTO blackOrWhiteDTO : blackOrWhiteDTOS) {
                redisTemplate.opsForValue().set(blackOrWhiteDTO.getBlackOrWhiteAccount(),blackOrWhiteDTO);
                accountList.add(blackOrWhiteDTO.getBlackOrWhiteAccount());
            }
        }catch (Exception ex){
            // clear redis data had been setted
            accountList.stream().forEach(account->{
                        redisTemplate.delete(account);
            }
            );
            throw new BusinessException(CommonErrorCode.CACHE_REDIS_OPT_ERROR,ex);
        }

        return Boolean.TRUE;
    }

    @Override
    public BlackOrWhiteDTO queryById(Long id) {
            BlackOrWhite blackOrWhite = blackOrWhiteMapper.selectByPrimaryKey(id);
            return blackOrWhite!=null ? BeanUtil.copy(blackOrWhite, BlackOrWhiteDTO.class): new BlackOrWhiteDTO();
    }

    @Override
    public BlackOrWhiteVO queryTest(BlackOrWhiteQuery query) {
        List<BlackOrWhiteVO> blackOrWhiteVOS = this.commonQuery(query);
        return CollectionUtils.isEmpty(blackOrWhiteVOS) ?  new BlackOrWhiteVO():blackOrWhiteVOS.get(0);
    }

    @Override
    public Boolean batchAdd(List<BlackOrWhiteDTO> blackOrWhiteDTOList) {
        blackOrWhiteDTOList.stream().forEach(item -> {
            this.add(item);
        });
        return Boolean.TRUE;
    }
}
